﻿using System;
using System.Linq;
using RayDen.Library.Components.Surface.Measured;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Data;
using RayDen.Library.Data.Utility;
using RayDen.Library.Entity.Frames;
using RayDen.Library.Entity.Scene;

namespace RayDen.Library.Components.Surface
{
    public class SurfaceMaterials
    {
        public static SurfaceMaterial MatteMaterial = new SurfaceMaterial(
            new MatteMaterial(new RgbSpectrum(0.65f)))
        {
            Name = "DefaultLambert",
            Type = BrdfType.Diffuse
        };

        public static SurfaceMaterial CreateFrameMaterial(FrameMaterialInfo mi)
        {
            BrdfClass brdfClass;
            if (!BrdfClass.TryParse(mi.BrdfClass, true, out brdfClass))
            {
                throw new ArgumentException("Invalid brdf class");
            }

            var matInfo = new MaterialInfo()
            {
                DataFile = mi.DataFile,
                Kd = mi.Diffuse,
                Kr = mi.Specular,
                Ks = mi.Glossy,
                PhongExponent = mi.Exponent,
                Name = mi.MaterialName,
                DiffuseTexture = new ImageTextureInfo() { FilePath = mi.DiffuseTexture },
                BumpTexture = new ImageTextureInfo() { FilePath = mi.BumpTexture },
                AlphaTexture = new ImageTextureInfo() { FilePath = mi.AlphaTexture },

            };

            return CreateMaterial(brdfClass, matInfo);
        }

        public static SurfaceMaterial CreateMaterial(MaterialInfo materialInfo)
        {
            var mat = CreateMaterialInternal(materialInfo);

            return mat;
        }

        //TODO Eval by mapper only elementary brdf - single return with variable parameters
        private static SurfaceMaterial CreateMaterialInternal(MaterialInfo materialInfo)
        {
            try
            {
                if (materialInfo.Name.ToLowerInvariant().StartsWith("mb") && materialInfo.Name.Length < 5)
                {
                    var index = int.Parse(materialInfo.Name.Substring(2, materialInfo.Name.Length - 2));
                    var mbData = SampledSpectrum.macbethSpectra[index].Select(i => (float)i).ToArray();
                    float[] mRes = new float[81];
                    for (int i = 0; i < 81; i++)
                    {
                        mRes[i] = (float)SampledSpectrum.macbethSpectra[i][index - 1];
                    }
                    SampledSpectrum spd =
                        SampledSpectrum.FromSampled(SampledSpectrum.macbeth_lambda.Select(o => (float)o).ToArray(),
                                                    mRes);
                    materialInfo.Reflectance = spd;
                    return new SurfaceMaterial(
                        //new OrenNayarMaterial(materialInfo.DiffuseReflectance, materialInfo.Kd.Filter())
                                new SpectralMatteMaterial(spd) { MaterialData = materialInfo })
                                    {
                                        Name = materialInfo.Name,
                                        Class = BrdfClassNameMapper.GetMaterialClass(materialInfo),
                                        Type = BrdfType.Diffuse,
                                    };
                }
            }
            catch { }

            if (materialInfo.Name.Contains("translucent"))
            {
                if (materialInfo.Name.Contains("alloy"))
                {
                    return new SurfaceMaterial(
                        new BrdfToBtdf(
                            new AlloyMaterial(materialInfo.DiffuseReflectance, materialInfo.GlossReflectance,
                                materialInfo.PhongExponent,
                                materialInfo.DiffuseReflectance.y() / materialInfo.GlossReflectance.y(), false)
                            {
                                MaterialData = materialInfo
                            }))
                    {
                        Name = materialInfo.Name.ToLower(),
                        Class = BrdfClassNameMapper.GetMaterialClass(materialInfo),
                        Type = BrdfType.Glossy | BrdfType.Refractive
                    };
                }
                return new SurfaceMaterial(
                    new BrdfToBtdf(new MatteMirrorMaterial(materialInfo.DiffuseReflectance,
                        materialInfo.GlossReflectance, false) { MaterialData = materialInfo }))
                    {
                        Name = materialInfo.Name.ToLower(),
                        Class = BrdfClassNameMapper.GetMaterialClass(materialInfo),
                        Type = BrdfType.Glossy
                    };

            }

            if (materialInfo.Name.NameContains("water"))
            {
                return new SurfaceMaterial(
                       new GlassMaterial(new RgbSpectrum(0f, 0.1f, 0.7f), materialInfo.GlossReflectance + 0.1f, 1.0005f, 1.31f, true, true)
                       {
                           MaterialData = materialInfo
                       })
                {
                    Name = materialInfo.Name.ToLower(),
                    Class = BrdfClassNameMapper.GetMaterialClass(materialInfo),
                    Type = BrdfType.Specular | BrdfType.Refractive
                };
            }

            switch (BrdfClassNameMapper.GetMaterialClass(materialInfo))
            {

                case BrdfClass.Measured:
                    return new SurfaceMaterial(
                        new RegularHalfangleBRDF(materialInfo.DataFile ?? @"F:\3D\Brdfs\merl\two-layer-silver.binary")
                        )
                        {
                            Class = BrdfClassNameMapper.GetMaterialClass(materialInfo),
                            Name = materialInfo.Name.ToLower(),
                            Type = BrdfType.Glossy
                        }; ;
                case BrdfClass.ArchGlass:
                    return new SurfaceMaterial(
                          new ArchGlassMaterial(materialInfo.DiffuseReflectance, materialInfo.GlossReflectance, true, true)
                          {
                              MaterialData = materialInfo
                          })
                            {
                                Name = materialInfo.Name.ToLower(),
                                Class = BrdfClassNameMapper.GetMaterialClass(materialInfo),
                                Type = BrdfType.Specular | BrdfType.Refractive
                            };
                case BrdfClass.Glass:
                   
                    return new SurfaceMaterial(
                        //new CanonicalGlassMaterial(materialInfo.Kd + 0.5f , materialInfo.GlossReflectance + 0.1f, MediumInfo.Glass)

                               new GlassMaterial(materialInfo.Kd + 1f, materialInfo.GlossReflectance + 0.1f, 1.0005f, materialInfo.Name.NameContains("flint") ? 1.98f : Math.Max(materialInfo.MediumInfo.IoR,1.5f), true, true)
                               //new DispersiveGlassMaterial(materialInfo.Kd + 1f, materialInfo.GlossReflectance + 0.1f, 1.0005f, 1.2f, true, true)

                          //new ArchGlassMaterial(materialInfo.DiffuseReflectance, materialInfo.GlossReflectance, true, true)

                               {
                                   MaterialData = materialInfo
                               })
                                {
                                    Name = materialInfo.Name.ToLower(),
                                    Class = BrdfClassNameMapper.GetMaterialClass(materialInfo),
                                    Type = BrdfType.Specular | BrdfType.Refractive
                                };
                case BrdfClass.GlossyMetal:
                    return new SurfaceMaterial(
                   new MetalMaterial(materialInfo.DiffuseReflectance, materialInfo.PhongExponent, true)
                   {
                       MaterialData = materialInfo
                   })
                    {
                        Name = materialInfo.Name.ToLower(),
                        Class = BrdfClassNameMapper.GetMaterialClass(materialInfo),
                        Type = BrdfType.Glossy
                    };
                case BrdfClass.Alloy:
                    bool specular = !materialInfo.Name.NameContains("dull");
                    return new SurfaceMaterial(
                        //new AnisotropicPhong(materialInfo.DiffuseReflectance, materialInfo.GlossReflectance, 20f, 20f)
                        //new PhongMaterial(RgbSpectrum.Max(ref materialInfo.Kd, ref materialInfo .Ks), materialInfo.PhongExponent)

                    new AlloyMaterial(materialInfo.DiffuseReflectance, materialInfo.GlossReflectance, materialInfo.PhongExponent, materialInfo.DiffuseReflectance.y() / materialInfo.GlossReflectance.y(), specular)
                        {
                            MaterialData = materialInfo
                        })
                        {
                            Name = materialInfo.Name.ToLower(),
                            Class = BrdfClassNameMapper.GetMaterialClass(materialInfo),
                            Type = BrdfType.Glossy
                        };
                case BrdfClass.Mirror:
                    return new SurfaceMaterial(
                   new MirrorMaterial(materialInfo.DiffuseReflectance, true) { MaterialData = materialInfo })
                   {
                       Name = materialInfo.Name.ToLower(),
                       Class = BrdfClassNameMapper.GetMaterialClass(materialInfo),
                       Type = BrdfType.Specular
                   };
                case BrdfClass.MatteMirror:
                    return new SurfaceMaterial(
                        new MatteMirrorMaterial(materialInfo.DiffuseReflectance, materialInfo.GlossReflectance, true) { MaterialData = materialInfo })
                    {
                        Name = materialInfo.Name.ToLower(),
                        Class = BrdfClassNameMapper.GetMaterialClass(materialInfo),
                        Type = BrdfType.Glossy
                    };
                case BrdfClass.Distribution:
                    //             return new RoughMatte(ref materialInfo.Kd) {Type = BrdfType.Diffuse};
                    return new SurfaceMaterial(
                        //new GlassMaterial(materialInfo.GlossReflectance, materialInfo.DiffuseReflectance + 0.1f, 1.0005f, 1.5f, true, true)
                        //new AnisotropicPhong(materialInfo.DiffuseReflectance, materialInfo.GlossReflectance , 10f, 10f)
                        //new PhongMaterial(materialInfo.Kd, materialInfo.Ks, materialInfo.PhongExponent)
                        //new FresnelBlend(materialInfo.Kd, materialInfo.Ks, materialInfo.PhongExponent)
                        //new MatteMaterial(materialInfo.DiffuseReflectance)
                        //new OrenNayarMaterial(materialInfo.DiffuseReflectance, materialInfo.Kd.Filter())
                        //new MatteMaterial(materialInfo.DiffuseReflectance) 
                        //new MatteMirrorMaterial( materialInfo.DiffuseReflectance, materialInfo.GlossReflectance, true)
                        //new MirrorMaterial(materialInfo.DiffuseReflectance,true)
                        //new DistributionBsdf(materialInfo.PhongExponent, materialInfo.Kd)
               new DistributionBsdf(materialInfo.PhongExponent, materialInfo.Kd + 0.5f, new FresnelConductor(new RgbSpectrum(0.5f), new RgbSpectrum(0.025f)))
                        //new ArchGlassMaterial(materialInfo.Kd, materialInfo.Ks, true, true) { MaterialData = materialInfo }
                        //new AlloyMaterial(materialInfo.Kd, materialInfo.Ks, materialInfo.Exponent, 0.5f, true)

               //new MetalMaterial(materialInfo.Kd, materialInfo.Exponent, true) { MaterialData = materialInfo }
                        //new MatteMaterial(materialInfo.Kd) { MaterialData = materialInfo }
             )
           {
               Name = materialInfo.Name,
               Class = BrdfClassNameMapper.GetMaterialClass(materialInfo),
               Type = BrdfType.Glossy | BrdfType.Diffuse,

           };
                case BrdfClass.DiffuseLambert:
                    return new SurfaceMaterial(
                        //new DistributionBsdf(materialInfo.Exponent, materialInfo.Kd)
                        //new OrenNayarMaterial(materialInfo.DiffuseReflectance, materialInfo.Kd.Filter())
                  new MatteMaterial(materialInfo.DiffuseReflectance) { MaterialData = materialInfo })
                  {
                      Name = materialInfo.Name,
                      Class = BrdfClassNameMapper.GetMaterialClass(materialInfo),
                      Type = BrdfType.Diffuse,

                  };
                default:
                    return new SurfaceMaterial(
                        //new DistributionBsdf(materialInfo.Exponent, materialInfo.Kd)
                        //new OrenNayarMaterial(materialInfo.DiffuseReflectance, materialInfo.Kd.Filter()) { MaterialData = materialInfo })

                  new MatteMaterial(materialInfo.DiffuseReflectance) { MaterialData = materialInfo })
                  {
                      Name = materialInfo.Name,
                      Class = BrdfClassNameMapper.GetMaterialClass(materialInfo),
                      Type = BrdfType.Diffuse,
                  };
            }
        }


        private static SurfaceMaterial CreateMaterial(BrdfClass cl, MaterialInfo materialInfo)
        {

            switch (cl)
            {

                case BrdfClass.Measured:
                    return new SurfaceMaterial(
                        new RegularHalfangleBRDF(materialInfo.DataFile ?? @"F:\3D\Brdfs\merl\two-layer-silver.binary")
                        )
                    {
                        Name = materialInfo.Name.ToLower(),
                        Type = BrdfType.Glossy
                    }; ;
                case BrdfClass.ArchGlass:
                    return new SurfaceMaterial(
                          new ArchGlassMaterial(materialInfo.DiffuseReflectance, materialInfo.GlossReflectance, true, true)
                          {
                              MaterialData = materialInfo
                          })
                    {
                        Name = materialInfo.Name.ToLower(),
                        Type = BrdfType.Specular
                    };
                case BrdfClass.Glass:
                    return new SurfaceMaterial(
                        //new CanonicalGlassMaterial(materialInfo.Kd + 0.5f , materialInfo.GlossReflectance + 0.1f, MediumInfo.Glass)
                               new GlassMaterial(materialInfo.Kd + 1f, materialInfo.GlossReflectance + 0.1f, 1.0005f, 1.2f, true, true)
                               {
                                   MaterialData = materialInfo
                               })
                    {
                        Name = materialInfo.Name.ToLower(),
                        Type = BrdfType.Specular
                    };
                case BrdfClass.GlossyMetal:
                    return new SurfaceMaterial(
                   new MetalMaterial(materialInfo.DiffuseReflectance, materialInfo.PhongExponent, true)
                   {
                       MaterialData = materialInfo
                   })
                    {
                        Name = materialInfo.Name.ToLower(),
                        Type = BrdfType.Glossy
                    };
                case BrdfClass.Alloy:
                    return new SurfaceMaterial(
                        //new AnisotropicPhong(materialInfo.DiffuseReflectance, materialInfo.GlossReflectance, 20f, 20f)
                        //new PhongMaterial(RgbSpectrum.Max(ref materialInfo.Kd, ref materialInfo .Ks), materialInfo.PhongExponent)

                    new AlloyMaterial(materialInfo.DiffuseReflectance, materialInfo.GlossReflectance, materialInfo.PhongExponent, 0.5f, true)
                    {
                        MaterialData = materialInfo
                    }) { Name = materialInfo.Name.ToLower(), Type = BrdfType.Glossy };
                case BrdfClass.Mirror:
                    return new SurfaceMaterial(
                   new MirrorMaterial(materialInfo.DiffuseReflectance, true) { MaterialData = materialInfo })
                    {
                        Name = materialInfo.Name.ToLower(),
                        Type = BrdfType.Specular
                    };
                case BrdfClass.MatteMirror:
                    return new SurfaceMaterial(
                        new MatteMirrorMaterial(materialInfo.DiffuseReflectance, materialInfo.GlossReflectance, true) { MaterialData = materialInfo })
                    {
                        Name = materialInfo.Name.ToLower(),
                        Type = BrdfType.Glossy
                    };
                case BrdfClass.Distribution:
                    //             return new RoughMatte(ref materialInfo.Kd) {Type = BrdfType.Diffuse};
                    return new SurfaceMaterial(
                        //new GlassMaterial(materialInfo.GlossReflectance, materialInfo.DiffuseReflectance + 0.1f, 1.0005f, 1.5f, true, true)
                        //new AnisotropicPhong(materialInfo.DiffuseReflectance, materialInfo.GlossReflectance , 10f, 10f)
                        //new PhongMaterial(materialInfo.Kd, materialInfo.Ks, materialInfo.PhongExponent)
                        //new FresnelBlend(materialInfo.Kd, materialInfo.Ks, materialInfo.PhongExponent)
                        //new MatteMaterial(materialInfo.DiffuseReflectance)
                        //new OrenNayarMaterial(materialInfo.DiffuseReflectance, materialInfo.Kd.Filter())
                        //new MatteMaterial(materialInfo.DiffuseReflectance) 
                        //new MatteMirrorMaterial( materialInfo.DiffuseReflectance, materialInfo.GlossReflectance, true)
                        //new MirrorMaterial(materialInfo.DiffuseReflectance,true)
                        //new DistributionBsdf(materialInfo.PhongExponent, materialInfo.Kd)
               new DistributionBsdf(materialInfo.PhongExponent, materialInfo.Kd + 0.5f, new FresnelConductor(new RgbSpectrum(0.5f), new RgbSpectrum(0.025f)))
                        //new ArchGlassMaterial(materialInfo.Kd, materialInfo.Ks, true, true) { MaterialData = materialInfo }
                        //new AlloyMaterial(materialInfo.Kd, materialInfo.Ks, materialInfo.Exponent, 0.5f, true)

               //new MetalMaterial(materialInfo.Kd, materialInfo.Exponent, true) { MaterialData = materialInfo }
                        //new MatteMaterial(materialInfo.Kd) { MaterialData = materialInfo }
             )
                    {
                        Name = materialInfo.Name,
                        Type = BrdfType.Glossy | BrdfType.Diffuse,

                    };
                case BrdfClass.DiffuseLambert:
                    return new SurfaceMaterial(
                        //new DistributionBsdf(materialInfo.Exponent, materialInfo.Kd)
                        //new OrenNayarMaterial(materialInfo.DiffuseReflectance, materialInfo.Kd.Filter())
                  new MatteMaterial(materialInfo.DiffuseReflectance) { MaterialData = materialInfo })
                    {
                        Name = materialInfo.Name,
                        Type = BrdfType.Diffuse,

                    };
                default:
                    return new SurfaceMaterial(
                        //new DistributionBsdf(materialInfo.Exponent, materialInfo.Kd)
                  new OrenNayarMaterial(materialInfo.DiffuseReflectance, materialInfo.Kd.Filter()) { MaterialData = materialInfo })

                  //new MatteMaterial(materialInfo.DiffuseReflectance) { MaterialData = materialInfo })
                    {
                        Name = materialInfo.Name,
                        Type = BrdfType.Diffuse,
                    };
            }
        }
    }
}

